package com.firefly.admin.login.service.impl;

import com.firefly.admin.common.utils.*;
import com.firefly.admin.login.service.LoginService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import com.firefly.admin.common.constant.Constants;
import com.firefly.admin.common.enums.ResultCodeEnum;
import com.firefly.admin.common.vo.LoginUser;
import com.firefly.admin.common.vo.Result;
import com.firefly.admin.login.vo.LoginInfoVo;
import com.firefly.admin.login.vo.RouterInfoVo;
import com.firefly.admin.login.vo.UserInfoVo;
import com.firefly.admin.system.entity.FySystemMenu;
import com.firefly.admin.system.entity.FySystemRole;
import com.firefly.admin.system.entity.FySystemUser;
import com.firefly.admin.system.service.FySystemMenuService;
import com.firefly.admin.system.service.FySystemRoleService;
import com.firefly.admin.system.service.FySystemUserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.authentication.logout.SecurityContextLogoutHandler;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;

@Service
@Slf4j
public class LoginServiceImpl implements LoginService {
    @Autowired
    private FySystemUserService fySystemUserService;
    @Autowired
    private FySystemRoleService fySystemRoleService;
    @Autowired
    private FySystemMenuService fySystemMenuService;

    /**
     * 系统管理员登录接口
     *
     * @param loginInfoVo 登录信息
     * @return 登录结果
     */
    @Override
    public Result<String> login(LoginInfoVo loginInfoVo) {
        if (loginInfoVo == null) {
            return Result.getResult(ResultCodeEnum.LOGIN_NAME_OR_PASSWORD_ERROR);
        }
        String userName = loginInfoVo.getUserName();
        if (StringUtils.isBlank(userName)) {
            return Result.getResult(ResultCodeEnum.LOGIN_USER_NAME_NOT_NULL);
        }
        String loginPwd = loginInfoVo.getLoginPwd();
        if (StringUtils.isBlank(loginPwd)) {
            return Result.getResult(ResultCodeEnum.LOGIN_PASSWORD_NOT_NULL);
        }
        log.info("##################[{}] login start!!!!!", userName);
        ResultCodeEnum checkResult = checkVerificationCode(loginInfoVo.getVerificationCode(), loginInfoVo.getVerificationCodeKey());
        if (checkResult != null) {
            return Result.getResult(checkResult);
        }
        List<FySystemUser> systemUserList = fySystemUserService.getSystemUserByUserName(userName);
        if (CollectionUtils.isEmpty(systemUserList) || systemUserList.size() > 1) {
            return Result.getResult(ResultCodeEnum.LOGIN_NAME_OR_PASSWORD_ERROR);
        }
        FySystemUser fySystemUser = systemUserList.get(0);
        if (StringUtils.isBlank(fySystemUser.getLoginPwd())) {
            return Result.getResult(ResultCodeEnum.LOGIN_NAME_OR_PASSWORD_EXCEPTION);
        }
        String dbLoginPwd = Sm2Util.decryptData(fySystemUser.getLoginPwd());
        if (!Sm3Util.encrypt(dbLoginPwd).equals(loginPwd)) {
            return Result.getResult(ResultCodeEnum.LOGIN_NAME_OR_PASSWORD_ERROR);
        }
        String userStatus = fySystemUser.getUserStatus();
        if (!Constants.Status.ENABLE.equals(userStatus)) {
            return Result.getResult(ResultCodeEnum.USER_STATUS_EXCEPTION);
        }
        String userToken = StringUtil.getRandomUUID();
        String token = JwtTokenUtil.createToken(userName, userToken);
        if (StringUtils.isEmpty(token)) {
            return Result.getResult(ResultCodeEnum.LOGIN_EXCEPTION);
        }
        doLoginSuccess(fySystemUser, token, userToken);
        fySystemUserService.updateLastLoginTime(fySystemUser);
        return Result.success("success", token);
    }

    /**
     * 系统管理员登出接口
     *
     * @return 操作结果
     */
    @Override
    public Result<String> logout() {
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
        if (authentication != null) {
            new SecurityContextLogoutHandler().logout(RequestUtil.getRequest(), RequestUtil.getResponse(), authentication);
            String userKey = JwtTokenUtil.getUserKey();
            RedisUtil.del(userKey);
            RedisUtil.hdel(Constants.Redis.USER_TOKEN, userKey);
        }
        return Result.success();
    }

    /**
     * 获取用户信息结果
     *
     * @return 用户信息
     */
    @Override
    public Result<UserInfoVo> getLoginUser() {
        LoginUser loginUser = JwtTokenUtil.getLoginUser();
        if (loginUser == null) {
            return Result.getResult(ResultCodeEnum.NOT_LOGGED_IN);
        }
        FySystemUser fySystemUser = loginUser.getFySystemUser();
        UserInfoVo userInfoVo = new UserInfoVo();
        userInfoVo.setName(fySystemUser.getUserName());
        userInfoVo.setAvatar(fySystemUser.getHeadSculpture());
        userInfoVo.setRealName(fySystemUser.getRealName());
        userInfoVo.setUserId(fySystemUser.getUserId());
        userInfoVo.setMobile(fySystemUser.getMobile());
        List<FySystemMenu> systemMenuList = loginUser.getSystemMenuList();
        if (!CollectionUtils.isEmpty(systemMenuList)) {
            String[] menuCodeArray = systemMenuList.stream().filter(Objects::nonNull).map(FySystemMenu::getMenuCode).filter(StringUtils::isNotBlank).toArray(String[]::new);
            userInfoVo.setRoles(menuCodeArray);
        }
        return Result.success("获取用户信息成功", userInfoVo);
    }

    /**
     * 获取登录用户有权限的菜单接口
     *
     * @return 路由信息
     */
    @Override
    public Result<List<RouterInfoVo>> getMenuList() {
        LoginUser loginUser = JwtTokenUtil.getLoginUser();
        if (loginUser == null) {
            return Result.getResult(ResultCodeEnum.NOT_LOGGED_IN);
        }
        List<FySystemMenu> systemMenuList = loginUser.getSystemMenuList();
        if (CollectionUtils.isEmpty(systemMenuList)) {
            List<FySystemRole> systemRoleList = fySystemRoleService.getRoleList(loginUser.getFySystemUser().getUserId());
            if (!CollectionUtils.isEmpty(systemRoleList)) {
                List<Long> roleIds = systemRoleList.stream().map(FySystemRole::getRoleId).collect(Collectors.toList());
                systemMenuList = fySystemMenuService.getMenuListByRoleIds(roleIds);
            }
        }
        List<RouterInfoVo> routerInfoVoList = TreeUtil.makeRouter(systemMenuList.stream().filter(fySystemMenu -> fySystemMenu != null && Constants.PERMISSION_TYPE_MENU.equals(fySystemMenu.getMenuType())).collect(Collectors.toList()), Constants.MENU_TOP_NODE);
        return Result.success("获取用户有权限的菜单成功", routerInfoVoList);
    }

    /**
     * 获取验证码信息接口
     *
     * @return 验证码信息
     */
    @Override
    public Result<Map<String, String>> getVerifyCode() {
        return Result.success(ImgValidateCodeUtil.getImgCodeBaseCode(4));
    }

    /**
     * 登录成功后组装LoginUser信息
     *
     * @param fySystemUser 管理员信息
     * @param token        token
     * @param userToken    用户token
     */
    private void doLoginSuccess(FySystemUser fySystemUser, String token, String userToken) {
        //获取用户角色列表
        Long userId = fySystemUser.getUserId();
        List<FySystemRole> systemRoleList = fySystemRoleService.getRoleList(userId);
        List<FySystemMenu> systemMenuList = new ArrayList<>();
        if (!CollectionUtils.isEmpty(systemRoleList)) {
            List<Long> roleIds = systemRoleList.stream().map(FySystemRole::getRoleId).collect(Collectors.toList());
            systemMenuList = fySystemMenuService.getMenuListByRoleIds(roleIds);
        }
        LoginUser loginUser = new LoginUser();
        loginUser.setUserName(fySystemUser.getUserName());
        loginUser.setLoginPwd(fySystemUser.getLoginPwd());
        loginUser.setFySystemUser(fySystemUser);
        loginUser.setSystemMenuList(systemMenuList);
        loginUser.setLoginTime(DateUtil.getNowDate());
        loginUser.setToken(token);
        RedisUtil.set(userToken, loginUser, 30 * 60);
        RedisUtil.hset(Constants.Redis.USER_TOKEN, userToken, userId);
    }

    private ResultCodeEnum checkVerificationCode(String verificationCode, String verificationCodeKey) {
        try {
            String isOpenVerificationCode = PropertiesUtil.getValue(Constants.Properties.IS_OPEN_VERIFICATION_CODE);
            //当配置为N时关闭验证码，其他为开启验证码
            if (Constants.VerificationCode.NOT_OPEN_VERIFICATION_CODE.equals(isOpenVerificationCode)) {
                return null;
            }
            if (StringUtils.isEmpty(verificationCode)) {
                return ResultCodeEnum.VERIFICATION_CODE_NOT_NULL;
            }
            String verificationCodeRedis = (String) RedisUtil.get(verificationCodeKey);
            RedisUtil.del(verificationCodeKey);
            if (StringUtils.isEmpty(verificationCodeRedis)) {
                return ResultCodeEnum.VERIFICATION_CODE_EXPIRED;
            }
            if (verificationCode.equals(verificationCodeRedis)) {
                return null;
            }
        } catch (Exception e) {
            return ResultCodeEnum.VERIFICATION_CODE_ERROR;
        }
        return ResultCodeEnum.VERIFICATION_CODE_ERROR;
    }
}
